/***********************************************************************************
 * 文件名： lcd.c
 * 作者： 刘言
 * 版本： 1
 * 说明：
 *      通用的LCD驱动程序。提供丰富的LCD操作API，详见头文件。
 * 		使用说明：修改配置文件，选择正确的芯片型号和LCD模块型号的C文件添加到工程编译。
 * 		移植说明：参照现有的芯片文件夹中的文件，创建芯片文件夹、芯片文件、LCD模块文件。
 * 修改记录：
 * 	2024/01/09: 初版。 刘言。
***********************************************************************************/
#include "lcd.h"
#if LCD_SHOW_LOGO
#include LCD_LOGO_ICLUDE
#endif
#include "polsys.h"


#if LCD_SHOW_LOGO
#define _LCD_LOGO_X     ((LCD_SIZE_X - LCD_LOGO_SIZE_X) / 2)
#define _LCD_LOGO_Y     ((LCD_SIZE_Y - LCD_LOGO_SIZE_Y) / 2)
#endif

// 外部函数申明

// 芯片初始化
void Lcd_ChipInit();


// 内部函数声明
#if LCD_SHOW_LOGO
static void ShowLogo();
#endif

// LCD统一初始化函数
void Lcd_Init(bool dis_on)
{
	_Lcd_BkLightOff();
#ifdef LCD_USE_CUSTOM_INIT_FUNC
	_Lcd_ChipInit();
#else
    Lcd_ChipInit();
#endif
    Lcd_SetWindow(0, 0, LCD_SIZE_X-1, LCD_SIZE_Y-1);
    Lcd_FillColor(Lcd_GetColorHex(LCD_BG_COLOR), LCD_SIZE_X * LCD_SIZE_Y);
#if LCD_SHOW_LOGO
	Lcd_DispOn();
    ShowLogo();
	Mcu_Delayms(LCD_LOGO_TIME);
#endif
	Lcd_SetPaintColorHex(0XFFFFFF);
#ifdef LCD_USE_OPT_API
	Lcd_SetFont(&Simsun12);
#endif
	if(dis_on)Lcd_DispOn();
}


#define FILL_BUFF_SIZE      64
void Lcd_FillColor(lcd_color_t color, u32 points)
{
    lcd_color_t buff[FILL_BUFF_SIZE];
    u32 i;

	if(points == 0)return;

    for(i = 0; i < FILL_BUFF_SIZE; i++)
    {
        buff[i] = color;
    }
    for(i = 0; i < points / FILL_BUFF_SIZE; i++)
    {
        while(_Lcd_PortBusy()){}
        _Lcd_PortWrite((u8 *)buff, sizeof(buff));
        // Delay_ms(30);
    }
    i = (points % FILL_BUFF_SIZE) * (LCD_COLOR_BITS/8);
    while(_Lcd_PortBusy()){}
    _Lcd_PortWrite((u8 *)buff, i);
    while(_Lcd_PortBusy()){}
}

#ifdef LCD_USE_OPT_API

static lcd_color_t mBgColor;
static lcd_color_t mPaintColor;

static const lcd_font_t * mFont;
static u16 mAsciiRowLen;	// 一个ASCII字符每一行点阵数据的长度（字节数）
static u16 mAsciiDotLen;	// 一个ASCII字符点阵数据的长度（字节数）
#ifdef LCD_USE_CH_CHAR
static u16 mChRowLen;	// 一个汉字每一行点阵的数据长度（字节数）
static u16 mChDotLen;	// 一个汉字点阵数据的长度（字节数）
#endif
static lcd_font_mode_t mFontMode = LCD_FM_TRANS;	// 字体的显示模式

inline void Lcd_SetBgColor(lcd_color_t c)
{
    mBgColor = c;
}

inline void Lcd_SetBgColorHex(u32 c)
{
    mBgColor = Lcd_GetColorHex(c);
}

inline void Lcd_SetPaintColor(lcd_color_t c)
{
    mPaintColor = c;
}

inline void Lcd_SetPaintColorHex(u32 c)
{
    mPaintColor = Lcd_GetColorHex(c);
}

inline void Lcd_SetFontMode(lcd_font_mode_t fm_mode)
{
	mFontMode = fm_mode;
}

/**
 * 画一个文字，不画背景
*/
static void DrawFont(const u8 *dot_lib, u16 x, u16 y, u8 font_type)
{
	u16 row;	// 行 y
	u16 col;	// 列 x
	u16 total_byte_ind = 0;
	u16 height, width, row_len; // 高度、宽度、每一行字节数

#ifdef LCD_USE_CH_CHAR
	if(font_type == 0)
#endif
	{
		height = mFont->AsciiHeight;
		width = mFont->AsciiWidth;
		row_len = mAsciiRowLen;
	}
#ifdef LCD_USE_CH_CHAR
	else
	{
		height = mFont->ChHeight;
		width = mFont->ChWidth;
		row_len = mChRowLen;
	}
#endif
	for(row = 0; row < height; row++)	// 逐行画点
	{
		u8 row_byte_ind; // 指示每行第几个字节
		u8 bit_ind;	// 指示每个字节第几个位（高位0）
		col = 0;
		for(row_byte_ind = 0; row_byte_ind < row_len; row_byte_ind++)	// 一个一个字节处理
		{
			u8 dat = dot_lib[total_byte_ind++];
			for(bit_ind = 0; bit_ind < 8; bit_ind++)
			{
				if(dat & 0x80 >> bit_ind)
				{
					Lcd_SetPoint(x + col, y + row, mPaintColor);
				}
				col++;
				if(col == width)break;
			}
		}
	}
}

/**
 * 画一个文字，使用背景色填充背景，通常速度更快
*/
static void DrawFontBg(const u8 *dot_lib, u16 x, u16 y, u8 font_type)
{
	u16 row;	// 行 y
	u16 col;	// 列 x
	u16 total_byte_ind = 0;
	u16 height, width, row_len; // 高度、宽度、每一行字节数

#ifdef LCD_USE_CH_CHAR
	if(font_type == 0)
#endif
	{
		height = mFont->AsciiHeight;
		width = mFont->AsciiWidth;
		row_len = mAsciiRowLen;
	}
#ifdef LCD_USE_CH_CHAR
	else
	{
		height = mFont->ChHeight;
		width = mFont->ChWidth;
		row_len = mChRowLen;
	}
#endif
	Lcd_SetWindow(x, y, x + width - 1, y + height - 1);
	for(row = 0; row < height; row++)	// 逐行画点
	{
		u8 row_byte_ind; // 指示每行第几个字节
		u8 bit_ind;	// 指示每个字节第几个位（高位0）
		col = 0;
		for(row_byte_ind = 0; row_byte_ind < row_len; row_byte_ind++)	// 一个一个字节处理
		{
			u8 dat = dot_lib[total_byte_ind++];
			for(bit_ind = 0; bit_ind < 8; bit_ind++)
			{
				if(dat & 0x80 >> bit_ind)
				{
					while(_Lcd_PortBusy()){}
					_Lcd_PortWrite((u8 *)&mPaintColor, sizeof(lcd_color_t));
				}
				else
				{
					while(_Lcd_PortBusy()){}
					_Lcd_PortWrite((u8 *)&mBgColor, sizeof(lcd_color_t));
				}
				col++;
				if(col == width)break;
			}
		}
	}
	Lcd_SetWindow(0, 0, LCD_SIZE_X-1, LCD_SIZE_Y-1);
}

#ifdef LCD_USE_CH_CHAR
/**
 * 画一个汉字
*/
static void PutCh(u16 x, u16 y, int gb_code)
{
	u16 char_ind;

	// 搜索字库中是否支持该汉字
	for(char_ind = 0; char_ind < mFont->ChNum; char_ind++)
	{
		if(gb_code == mFont->ChCodeList[char_ind])break; //找到了，跳出循环
	}

	if(char_ind == mFont->ChNum)	// 字库中没有这个汉字，全部填充
	{
		Lcd_Fill(x, y, x + mFont->ChWidth, y + mFont->ChHeight);
	}
	else	// 字库中有这个汉字
	{
		const u8 *dot_lib = mFont->ChLib + char_ind * mChDotLen;
		if(mFontMode == LCD_FM_TRANS)DrawFont(dot_lib, x, y, 1);
		else DrawFontBg(dot_lib, x, y, 1);
	}
}
#endif

/**
 * 画一个ASCII字符
*/
static void PutAscii(u16 x, u16 y, char c)
{
	const u8 *dot_lib = mFont->AsciiLib + (c - 0x20) * mAsciiDotLen;
	if(mFontMode == LCD_FM_TRANS)DrawFont(dot_lib, x, y, 0);
	else DrawFontBg(dot_lib, x, y, 0);
}

void Lcd_ShowString(u16 x, u16 y, const char *s)
{
	while(*s != '\0')
	{
#ifdef LCD_USE_CH_CHAR
		if((u8)*s > 0x80)	// 汉字码
		{
			if(x > LCD_SIZE_X - mFont->ChWidth)
			{	
				y += mFont->ChHeight + mFont->LineSpace;
				x = 0;
			}
			PutCh(x, y, *((u16 *)s));
			s += 2;
			x = x + mFont->ChWidth + mFont->WordSpace;
		}
		else
		{			//ASCII码
#endif
			if(x > LCD_SIZE_X - mFont->AsciiWidth)
			{
				y += mFont->AsciiHeight + mFont->LineSpace;
				x = 0;
			}
			PutAscii(x, y, *s);
			s++;
			x = x + mFont->AsciiWidth + mFont->WordSpace;
#ifdef LCD_USE_CH_CHAR
		}
#endif
	}
}

void Lcd_SetFont(const lcd_font_t *font)
{
	mFont = font;
#ifdef LCD_USE_CH_CHAR
	mChRowLen = mFont->ChWidth / 8;
	if(mFont->ChWidth % 8 > 0)mChRowLen++;
	mChDotLen = mChRowLen * mFont->ChHeight;
#endif
	mAsciiRowLen = mFont->AsciiWidth / 8;
	if(mFont->AsciiWidth % 8 > 0)mAsciiRowLen++;
	mAsciiDotLen = mAsciiRowLen * mFont->AsciiHeight;
}

void Lcd_Line(u16 x0, u16 y0, u16 x1, u16 y1)
{
    u16 x,y;
 	u16 dx;// = abs(x1 - x0);
 	u16 dy;// = abs(y1 - y0);

	if(y0==y1)
	{
		if(x0<=x1)
		{
			x=x0;
		}
		else
		{
			x=x1;
			x1=x0;
		}
  		while(x <= x1)
  		{
   			Lcd_SetPoint(x,y0,mPaintColor);
   			x++;
  		}
  		return;
	}
	else if(y0>y1)
	{
		dy=y0-y1;
	}
	else
	{
		dy=y1-y0;
	}
 
 	if(x0==x1)
	{
		if(y0<=y1)
		{
			y=y0;
		}
		else
		{
			y=y1;
			y1=y0;
		}
  		while(y <= y1)
  		{
   			Lcd_SetPoint(x0,y,mPaintColor);
   			y++;
  		}
  		return;
	}
	else if(x0 > x1)
 	{
		dx=x0-x1;
  		x = x1;
  		x1 = x0;
  		y = y1;
  		y1 = y0;
 	}
 	else
 	{
		dx=x1-x0;
  		x = x0;
  		y = y0;
 	}

 	if(dx == dy)
 	{
  		while(x <= x1)
  		{

   			x++;
			if(y>y1)
			{
				y--;
			}
			else
			{
   				y++;
			}
   			Lcd_SetPoint(x,y,mPaintColor);
  		}
 	}
 	else
 	{
 		Lcd_SetPoint(x, y, mPaintColor);
  		if(y < y1)
  		{
   			if(dx > dy)
   			{
    			s16 p = dy * 2 - dx;
    			s16 twoDy = 2 * dy;
    			s16 twoDyMinusDx = 2 * (dy - dx);
    			while(x < x1)
    			{
     				x++;
     				if(p < 0)
     				{
      					p += twoDy;
     				}
     				else
     				{
      					y++;
      					p += twoDyMinusDx;
     				}
     				Lcd_SetPoint(x, y,mPaintColor);
    			}
   			}
   			else
   			{
    			s16 p = dx * 2 - dy;
    			s16 twoDx = 2 * dx;
    			s16 twoDxMinusDy = 2 * (dx - dy);
    			while(y < y1)
    			{
     				y++;
     				if(p < 0)
     				{
      					p += twoDx;
     				}
     				else
     				{
      					x++;
      					p+= twoDxMinusDy;
     				}
     				Lcd_SetPoint(x, y, mPaintColor);
    			}
   			}
  		}
  		else
  		{
   			if(dx > dy)
   			{
    			s16 p = dy * 2 - dx;
    			s16 twoDy = 2 * dy;
	    		s16 twoDyMinusDx = 2 * (dy - dx);
    			while(x < x1)
    			{
     				x++;
     				if(p < 0)
	     			{
    	  				p += twoDy;
     				}
     				else
     				{
      					y--;
	      				p += twoDyMinusDx;
    	 			}
     				Lcd_SetPoint(x, y,mPaintColor);
    			}
   			}
	   		else
   			{
    			s16 p = dx * 2 - dy;
    			s16 twoDx = 2 * dx;
	    		s16 twoDxMinusDy = 2 * (dx - dy);
    			while(y1 < y)
    			{
     				y--;
     				if(p < 0)
	     			{
    	  				p += twoDx;
     				}
     				else
     				{
      					x++;
	      				p+= twoDxMinusDy;
    	 			}
     				Lcd_SetPoint(x, y, mPaintColor);
    			}
   			}
  		}
 	}
}

void Lcd_Circle(u16 cx, u16 cy, u16 r, bool fill)
{
    u16 x,y;
	s16 delta,tmp;
	x=0;
	y=r;
	delta=3-(r<<1);

	while(y>x)
	{
		if(fill)
		{
			Lcd_Line(cx+x,cy+y,cx-x,cy+y);
			Lcd_Line(cx+x,cy-y,cx-x,cy-y);
			Lcd_Line(cx+y,cy+x,cx-y,cy+x);
			Lcd_Line(cx+y,cy-x,cx-y,cy-x);
		}
		else
		{
			Lcd_SetPoint(cx+x,cy+y,mPaintColor);
			Lcd_SetPoint(cx-x,cy+y,mPaintColor);
			Lcd_SetPoint(cx+x,cy-y,mPaintColor);
			Lcd_SetPoint(cx-x,cy-y,mPaintColor);
			Lcd_SetPoint(cx+y,cy+x,mPaintColor);
			Lcd_SetPoint(cx-y,cy+x,mPaintColor);
			Lcd_SetPoint(cx+y,cy-x,mPaintColor);
			Lcd_SetPoint(cx-y,cy-x,mPaintColor);
		}
		x++;
		if(delta>=0)
		{
			y--;
			tmp=(x<<2);
			tmp-=(y<<2);
			delta+=(tmp+10);
		}
		else
		{
			delta+=((x<<2)+6);
		}
	}
}

void Lcd_Rectangle(u16 x0, u16 y0, u16 x1, u16 y1)
{
    Lcd_Line(x0,y0,x0,y1);
	Lcd_Line(x0,y1,x1,y1);
	Lcd_Line(x1,y1,x1,y0);
	Lcd_Line(x1,y0,x0,y0);
}

void Lcd_Clear(u16 x0, u16 y0, u16 x1, u16 y1)
{
    Lcd_SetWindow(x0, y0, x1, y1);
    Lcd_FillColor(mBgColor, (u32)(x1-x0+1)*(y1-y0+1));
    Lcd_SetWindow(0, 0, LCD_SIZE_X-1, LCD_SIZE_Y-1);
}

void Lcd_Fill(u16 x0, u16 y0, u16 x1, u16 y1)
{
    Lcd_SetWindow(x0, y0, x1, y1);
    Lcd_FillColor(mPaintColor, (x1-x0+1)*(y1-y0+1));
    Lcd_SetWindow(0, 0, LCD_SIZE_X-1, LCD_SIZE_Y-1);
}

#endif

// 内部函数

#if LCD_SHOW_LOGO
static void ShowLogo()
{
    lcd_color_t buff[FILL_BUFF_SIZE];
    u32 px = 0;
    u16 buff_index;
    u16 img_index = 0;

    Lcd_SetWindow(_LCD_LOGO_X, _LCD_LOGO_Y, _LCD_LOGO_X + LCD_LOGO_SIZE_X-1, _LCD_LOGO_Y + LCD_LOGO_SIZE_Y-1);

    while(px < LCD_LOGO_SIZE_X * LCD_LOGO_SIZE_Y)
    {
        // 准备显示数据
        u16 i;
        buff_index = 0;
        for(i = 0; i < FILL_BUFF_SIZE / 8; i++)
        {
            u8 bit_index;
            for(bit_index = 0; bit_index < 8; bit_index++)
            {
                if(LCD_LOGO_BUFF[img_index] & (0x80 >> bit_index))buff[buff_index++] = Lcd_GetColorHex(LCD_LOGO_COLOR);
                else buff[buff_index++] = Lcd_GetColorHex(LCD_BG_COLOR);
            }
            img_index++;
        }
        // 写显示数据到LCD
        while(_Lcd_PortBusy());
        _Lcd_PortWrite((u8 *)buff, sizeof(buff));
        px += FILL_BUFF_SIZE;
    }
    Lcd_SetWindow(0, 0, LCD_SIZE_X-1, LCD_SIZE_Y-1);
}
#endif



